home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Commodore Free 14
/
Commodore_Free_Issue_14_2007_Commodore_Computer_Club.d64
/
t.hexfiles 9
< prev
next >
Wrap
File List
|
2023-02-26
|
12KB
|
405 lines
u
Hexfiles part 9
By Jason Kelk
http://www.oldschool-gaming.com
Ah, there you are. I wondered where
you'd got to. Ready to start playing
with that new project I promised last
issue then? Good, lets get on with it,
shall we? We're going to write a little
game. Don't worry, it's not going to be
Armalyte or anything complex like that,
we're just going to move a few sprites
around for a starter. As usual, there
is some source on the covermount, the
filename is game_1.asm, so fire up your
text editor & LOAD it in.
The majority of the source is
variations on routines we've covered in
the past so you should be happy with
them, so instead of dissecting the
entire program a line at a time I'm
just going to cover points of interest.
So, starting from the top of the source
& working down, the first stop is a
routine called makespr which fills the
last sprite of bank 0 ($3FC0 - $3FCF)
with $FF to make it a solid block. This
is just a temporary measure, eventually
there will be real definitions but for
now we just want to see where the
sprites are. A little later on in the
source (after the raster setup) is a
loop called setsdp that points all
eight sprites to $2800 for their data.
Right, now we have to set everything
up. First, a quick call to xpand (which
we'll cover in more detail soon), it is
a routine that sets all sprite
positions. Then comes something we've
not used before:
lda #$00
sta sync
strtwait cmp sync
beq strtwait
Now at first sight this seems totally
pointless, doesn't it? If sync is set
to $00 it's not suddenly going to
change whilst we're sitting in a loop
... Well, actually it is because the
second raster split, which is at line
$FC, sets sync to $01 once a frame,
this routine actually synchronises the
runtime code with the raster, which is
where the label gets it's name.
After that we read $D01E, which is the
sprite to sprite collision register,
but we don't do anything with the
values since we're merely reading it in
order to clear it for when the game
starts.
Next up is the main loop of the game &
it uses another loop to wait for sync
to get the game movement synchronised
to the raster interrupt; then we have
three calls to subroutines, joyread
reads the joystick in port 2, sprmove
moves the sprite data &, again, xpand
puts the sprite data into registers.
After the main processes we have
another read of $D01E but this time
we're going to actually be doing
something with the data. As with other
sprite registers, $D01E represents each
sprite with a bit, there are 8 sprites
& 8 bits just like the sprite enable
register $D015 (covered in part 4) but
these bits become set if 2 sprites
collide.
So if sprites 0 & 1 bump into each
other, $D01E is set to $03 (sprite 0
being the first bit & having a value of
$01, sprite 1 being the 2nd bit &
representing the value $02) & if
sprites 0 & 7 have a pile-up, $D01E
reads $81.
There is a problem with $D01E though;
if sprites 0 & 3 hit each other & at
the same time sprites 1 & 6 have a
prang, we can only see that sprite 0
has hit at least 1 of the others, not
which one(s). In fact, if all 4 sprites
hit each other the value in $D01E will
be the same as when they collide as 2
pairs so whilst that's not a problem
for what we're doing right now (since
we just need to know if sprite 0 has
hit something) later on it will get in
the way so this is only a temporary
measure & we'll be dropping the use of
hardware collisions fairly soon.
Anyway, back to the source & after that
read from $D01E we have a new command
called LSR, which means Logical Shift
Right. LSR moves all the bits in a byte
down one, so the highest bit, the one
that represents 128 moves down to the
64 position, the 64 to the 32 & so
forth.
The lowest bit, the one representing 1,
falls off the end & into the carry flag
& the highest bit is left un-set by
this operation. LSR has a number of
uses, not least of which is that it
basically divides any 8 bit number by
two. In our code we have an LSR A which
is C64Asm's way of saying that the
Logical Shift Right will happen to the
value in the accumulator, but LSR $4000
will work just as well & perform the
operation on the appropriate byte in
memory.
But why are we using LSR here? Well,
it's a quick & dirty way of checking if
the lowest bit of $D01E (the one
representing sprite 0, our "player"
sprite) is set. As I said, that low bit
will fall into the carry flag & the BCC
(Branch on Carry Clear) simply causes
the program to move back to the label
main if the carry flag is empty after
that operation.
If the carry isn't empty that means
sprite 0 is touching another sprite, so
we do a quick INC $D027 to change the
sprite colour for now to indicate that
a collision has happened & then head
back to main again.
The next part of the source is just the
stock raster routines we've covered
before, with raster2 setting sync to
let the runtime code know it's time to
start work before it calls $EA31. Then
we arrive at xpand... this is the most
complex routine in the entire piece of
code, it takes the sprite co-ordinates
from a table called sprtpos & puts them
into $D000 onwards, but it does a
little trickery to make handling the
MSB easy for the rest of the program as
well. This needs a complete breakdown
of the code, but I need to cover 2 more
new commands before that, ASL & ROR.
Accumulator Shift Left, or ASL to it's
friends, is basically the reverse of
LSR in that all the bits of a byte move
up a place; the 1 bit is left un-set &
the 128 bit gets nudged into the carry.
And, since it's the opposite of LSR, it
can also be thought of as multiplying
an 8 bit number by two (although any-
thing over $7f will need the 9th bit in
the carry taken care of to get a
correct answer). ROR, or ROtate Right,
is almost the same as LSR, except that
the previous contents of the carry get
pushed into the 128 bit before the
other end falls into it.
Both commands can work directly to the
accumulator or to memory as with LSR.
(There is a 4th command to this set,
ROL, which works like ASL but has the
contents of the carry pushed onto the
end like ROR & we'll come across that
at another time.)
Okay, so now for a line by line look at
xpand. the sprtpos table contains 16
bytes & they are stored as sprite 0's X
& Y, sprite 1's X & Y & so forth, the
same order as $D000 onwards uses. The
1st couple of lines just copy sprtpos+1
(the 1st sprite's Y position) straight
into the Y register: xpand ldx #$00 We
know this...
xpndloop lda
sprtpos+$01,X
Read the Y co-ordinate
sta $d001,x Set it into the
sprite Y position
But the next bit to read sprtpos+$00
(the first sprite's X position) is a
little more fiddly:
lda sprtpos+$00,x Read X co-ordinate
asl a Multiply by 2
Here we are using a trick that C=
designers came up with ages ago (the
same system is used for the X position
of the light pen register). Because the
screen is 320 pixels across we
have to use the MSB (as explained in
issue 10) but MSB handling is fiddly at
the best of times. So we're taking the
value in SPRTPOS & multiplying it by 2
using ASL. But that doesn't actually
sort out the MSB, does it? So...
ror $d010
Roll $d010 a bit to the right
...we move the contents of the carry
into the top of the MSB (at the 128
position). Because the loop runs 8
times the first bit in ends up in the
lowest position, the 2nd at the 2nd
lowest & so on, until the bit that
needs to represent sprite 7 is at the
highest position. Then we...
sta $d000,X Write to sprite X position
...write back the contents of the
accumulator to the sprite.
Finally we manage the loop:
inx Just a standard loop counter, but
inx we're going up in steps of2...
cpx #$10 ...until X reaches $10
bne xpndloop
And to finish, we count up in steps of
2 until we reach $10 & have, therefore,
run the loop 8 times - since 2 bytes
are transferred each iteration of the
loop, that's all 16 bytes of sprtpos
transferred during those 8 passes. This
loop may seem a little complex, but MSB
handling is a tricky job anyway &
although it sacrifices some of our
movement control horizontally, this
technique means that from here onwards
all sprite X co-ordinates are just a
value from $00 to $FF so we can do
quick & simple mathematics to them in
order to shift the sprites; a simple
INC to the first byte of sprpos once a
frame will move the 1st sprite right
all the way across the screen with no
extra work needed. There are situations
where it's necessary to make things
move at a single pixel a frame, but for
general useage, this goes & if it's
good enough for C= design team it's
good enough for us!
After the xpand routine comes joyread,
a routine to scan the joystick. 1st up
is a read from $DC00 & we're using LSR
again to move the bits off into the
carry one at a time to see what state
they're in. $DC00 actually works in
reverse, if the 1st bit is clear that
means that the joystich has been pushed
up so our sprite needs to react. How do
we test that?
With a Branch on Carry Set (BCS)
command; if the carry is set after the
LSR then the stick isn't being pushed
up & we branch over the routine that
moves the sprite up so that it doesn't
move that way. The same goes for the
other directions & then FIRE. The 5
joystick bits represent (lowest to
highest) Up, Down, Left, Right & FIRE.
So we move the sprite up like this:
ldx sprtpos+$01 Read sprite Y position
dex
dex
dex
dex Subtract four
cpx #$32 $32 is the top line of screen
bcs setup
BCS again? Well yes, due to the way the
6510 does mathematics, BCS will act as
a "greater or equal to" command here;
if the contents of the X register are
greater than or equal to $32 it'll
branch to setup.
ldx #$32
Since this will only happen if X is
less than $32 it makes sure X will
always be $32 or greater, so not into
the upper border.
setup stx sprtpos+$01
Store the X position back
The BASIC equivalent to the CMP, BCS &
LDX is IF Y<50 THEN Y=50
The down works in a similar manner,
except that it's adding to the X
register with four INXs & uses a BCC to
branch over the LDX #$E5 (the lowest
position a sprite can be at without
being under the lower border) since BCC
only works as a "less than" when used
after the compare, not "less than or
equal". (These tricks with BCC & BCS
work after any compare command, not
just those for the X register & can be
very useful.)
Again, Left & Right are the same basic
block of code as Up & Down, except that
it has different stop positions for the
edges of the screen &, because the X
position of the sprites gets multiplied
by 2 when they're displayed by xpand,
the X position is only changed by 2
rather than 4
Okay, we have one final loop to look
at, sprmove takes the contents of
sprtpos+$04 (sprite 3's X position) &
adds the contents of another table
called sprtspd to it, then goes through
the sprite positions until it does
sprite 7's Y position. Since sprtpos
contains the present positions of the
sprites & sprtspd contains their
"speed" settings, the value that is
added to each X or Y co-ordinate in
turn to make the sprites move.
This sprtspd table uses some trickery,
adding $FF, for example, to a byte will
actually cause the byte to "wrap"
around so the value goes down by 1.
This is where xpand really comes into
it's own, instead of having to know
which direction the sprite is moving &
have a routine to handle it we just
perform an add! Sneaky, eh?
Righto, that's your lot for this
installment, but next time we'll add
some new features to our game, like
some better collisions, an ingame
soundtrack & some nice graphics. LOAD
up game_2.asm to get the little game-
ette going with a sprite & some backing
music (as per how we did these things
with the demo previously, so you should
be able to work out for yourself how).
As always, contact me with any
questions & I'll see you next time,
matey.
The source code for the routines above
can be downloaded at:
http://www.oldschoolgaming.com
/content/files/hex_files
/part_9_files.zip
for easier reference.
C= Free would like to thank Jason for
granting permission to print the
article.
Unfortunately at the moment this is the
last article in the series, you could
contact Jason & request more if you
found the info useful.